package justweb

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"sync/atomic"
	"syscall"
	"time"
)

var (
	// ShutdownSignals receives shutdown signals to process
	ShutdownSignals = []os.Signal{
		os.Interrupt, os.Kill, syscall.SIGKILL,
		syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP,
		syscall.SIGABRT, syscall.SIGTERM,
	}
)

type ServerShutdown struct {
	closing   bool          // 是否要关闭 - true:关闭
	reqCnt    int64         // 当前的请求数量
	signalGet chan struct{} // 传递信号
}

// 构建关闭开关
func NewServerShutdown() *ServerShutdown {
	return &ServerShutdown{
		signalGet: make(chan struct{}),
	}
}

// 关闭请求  - 因为针对请求，可以设计成为中间件的形式
func (sd *ServerShutdown) ShutdownFilterBuilder(next Filter) Filter {
	return func(c *Context) {
		// 如果关闭了，就拒绝请求
		if sd.closing {
			c.W.WriteHeader(http.StatusServiceUnavailable)
			return // 直接返回
		}

		atomic.AddInt64(&sd.reqCnt, 1) // 原子操作增加请求数

		next(c)
		num := atomic.AddInt64(&sd.reqCnt, -1)
		// 关闭信号启动，并且剩余请求为0
		if num == 0 && sd.closing {
			sd.signalGet <- struct{}{} // 通知已经都处理完毕
		}
	}
}

// 信号监听 -- 并调度函数(可以调度 before \ after \ surrund )
func WaitForShutdown(hooks ...HookFunc) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, ShutdownSignals...)

	sig := <-signals // 监听关闭信号
	log.Println("get signal", sig, ", will shutdown the server")

	// 实现超时机制，多久之后还没有结束，那就强制结束
	time.AfterFunc(time.Second*10, func() {
		log.Println("shutdown and timeout , force shutdown")
		os.Exit(1)
	})

	// 开始调度
	for i := 0; i < len(hooks); i++ {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) // 注意这里存在超时问题
		err := hooks[i](ctx)
		if nil != err {
			log.Println("run the hook fail with err : ", err)
		}
		cancel()
	}

	os.Exit(0)
}

// 关闭的动作 -- 管理靠goroutine间的通信，借助context.Context包完成
func (sd *ServerShutdown) RejectReqAndWait(ctx context.Context) error {

	sd.closing = true // 设置为关闭

	// 如果目前状态是都处理完的，直接返回
	if sd.reqCnt == 0 {
		return nil
	}

	done := ctx.Done()

	select {
	case <-sd.signalGet:
		log.Println("请求完全处理完毕")

	case <-done:
		log.Println("timeout , the server interrept")
	}
	return nil
}

// ===================下面是hook函数，用来扩展=======================
type HookFunc func(context.Context) error

// 关闭单独服务server的hook函数
func CloseServerHook(s ...Server) HookFunc {
	return func(ctx context.Context) error {
		wg := sync.WaitGroup{} // 多个服务单独关闭

		doneCh := make(chan struct{})

		wg.Add(len(s))

		// 启动协程关闭各个服务
		for i := 0; i < len(s); i++ {
			go func(srv Server) {
				err := srv.Shutdown(ctx)
				if nil != err {
					fmt.Println("server shutdown error ", err)
				}
				time.Sleep(time.Second)
				wg.Done()
			}(s[i])
		}

		// 监控完成信号
		go func() {
			wg.Wait()
			doneCh <- struct{}{}
		}()

		// 等待处理
		select {
		case <-ctx.Done():
			fmt.Println("closing servers timeout")
			return ErrorHookTimeout
		case <-doneCh:
			fmt.Println("close all server successfully")
			return nil
		}
	}
}

// 通知网关下线
func NotifyThegateaway(ctx context.Context) error {
	fmt.Println("下线通知")
	time.Sleep(time.Second * 3)
	return nil
}

// 发送给消息中间件服务
func AfterShutdown(ctx context.Context) error {
	// ...
	pool, ok := GetGPool()
	if ok {
		pool.Free()
	}
	return nil
}
